#include "Utils.h"
#include "FnvHash.h"

int Utils::DEFAULT_BUFFER_SIZE = 1024 * 4;

// string_ptr  read(InputStream in) {
//     if (in == NULL) {
//         return NULL;
//     }

//     InputStreamReader reader;
//     try {
//         reader = new InputStreamReader(in, "UTF-8");
//     } catch (UnsupportedEncodingException e) {
//         throw new IllegalStateException(e.getMessage(), e);
//     }
//     return read(reader);
// }

// string_ptr  readFromResource(string_ptr  resource) throws IOException {
//     if (resource == NULL
//             || resource.isEmpty()
//             || resource.contains("..")
//             || resource.contains("?")
//             || resource.contains(":")) {
//         return NULL;
//     }

//     InputStream in = NULL;
//     try {
//         in = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource);
//         if (in == NULL) {
//             in = Utils.class.getResourceAsStream(resource);
//         }

//         if (in == NULL) {
//             return NULL;
//         }

//         string_ptr  text = Utils.read(in);
//         return text;
//     } finally {
//         JdbcUtils.close(in);
//     }
// }

// char* readByteArrayFromResource(string_ptr  resource)  {
//     if (resource.empty()
//             || resource.contains("..")
//             || resource.contains("?")
//             || resource.contains(":")) {
//         return NULL;
//     }

//     InputStream in = NULL;
//     try {
//         in = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource);
//         if (in == NULL) {
//             return NULL;
//         }

//         return readByteArray(in);
//     } finally {
//         JdbcUtils.close(in);
//     }
// }

// char* readByteArray(InputStream input) throws IOException {
//     if (input == NULL) {
//         return NULL;
//     }
//     ByteArrayOutputStream output = new ByteArrayOutputStream();
//     copy(input, output);
//     char* bytes = output.toByteArray();
//     output.close();
//     return bytes;
// }

// long copy(InputStream input, OutputStream output) throws IOException {
//     int EOF = -1;

//     char buffer[DEFAULT_BUFFER_SIZE];

//     long count = 0;
//     int n = 0;
//     while (EOF != (n = input.read(buffer))) {
//         output.write(buffer, 0, n);
//         count += n;
//     }
//     return count;
// }

// string_ptr  read(Reader reader) {
//     if (reader == NULL) {
//         return NULL;
//     }

//     try {
//         StringWriter writer = new StringWriter();

//         char buffer[DEFAULT_BUFFER_SIZE];
//         int n = 0;
//         while (-1 != (n = reader.read(buffer))) {
//             writer.write(buffer, 0, n);
//         }

//         return writer.toString();
//     } catch (IOException ex) {
//         throw new IllegalStateException("read error", ex);
//     }
// }

// string_ptr  read(Reader reader, int length) {
//     if (reader == NULL) {
//         return NULL;
//     }

//     try {
//         char buffer[length];

//         int offset = 0;
//         int rest = length;
//         int len;
//         while ((len = reader.read(buffer, offset, rest)) != -1) {
//             rest -= len;
//             offset += len;

//             if (rest == 0) {
//                 break;
//             }
//         }

//         return new string_ptr (buffer, 0, length - rest);
//     } catch (IOException ex) {
//         throw new IllegalStateException("read error", ex);
//     }
// }

// string_ptr  toString(java.util.Date date) {
//     if (date == NULL) {
//         return NULL;
//     }
//     return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
//             .format(date);
// }

// string_ptr  getStackTrace(Throwable ex) {
//     StringWriter buf = new StringWriter();
//     ex.printStackTrace(new PrintWriter(buf));
//     return buf.toString();
// }

// string_ptr  toString(StackTraceElement[] stackTrace) {
//     StringBuilder buf = new StringBuilder();
//     for (StackTraceElement item : stackTrace) {
//         buf.append(item.toString());
//         buf.append("\n");
//     }
//     return buf.toString();
// }

// Boolean getBoolean(Properties properties, string_ptr  key) {
//     string_ptr  property = properties.getProperty(key);
//     if ("true".equals(property)) {
//         return Boolean.TRUE;
//     } else if ("false".equals(property)) {
//         return Boolean.FALSE;
//     }
//     return NULL;
// }

// Integer getInteger(Properties properties, string_ptr  key) {
//     string_ptr  property = properties.getProperty(key);

//     if (property == NULL) {
//         return NULL;
//     }
//     try {
//         return Integer.parseInt(property);
//     } catch (NumberFormatException ex) {
//         // skip
//     }
//     return NULL;
// }

// Long getLong(Properties properties, string_ptr  key) {
//     string_ptr  property = properties.getProperty(key);

//     if (property == NULL) {
//         return NULL;
//     }
//     try {
//         return Long.parseLong(property);
//     } catch (NumberFormatException ex) {
//         // skip
//     }
//     return NULL;
// }

// Class<?> loadClass(string_ptr  className) {
//     Class<?> clazz = NULL;

//     if (className == NULL) {
//         return NULL;
//     }

//     try {
//         return Class.forName(className);
//     } catch (ClassNotFoundException e) {
//         // skip
//     }

//     ClassLoader ctxClassLoader = Thread.currentThread().getContextClassLoader();
//     if (ctxClassLoader != NULL) {
//         try {
//             clazz = ctxClassLoader.loadClass(className);
//         } catch (ClassNotFoundException e) {
//             // skip
//         }
//     }

//     return clazz;
// }

// time_t getStartTime() {
//     if (startTime == NULL) {
//         startTime = new Date(ManagementFactory.getRuntimeMXBean().getStartTime());
//     }
//     return startTime;
// }

// long murmurhash2_64(string_ptr  text) {
//     char* bytes = text.getBytes();
//     return murmurhash2_64(bytes, bytes.length, 0xe17a1465);
// }

/**
 * murmur hash 2.0, The murmur hash is a relatively fast hash function from http://murmurhash.googlepages.com/ for
 * platforms with efficient multiplication.
 *
 * @author Viliam Holub
 */
long Utils::murmurhash2_64(char *data, int length, int seed)
{
  long m = 0xc6a4a7935bd1e995L;
  int r = 47;

  long h = (seed & 0xffffffffL) ^ (length * m);

  int length8 = length / 8;

  for (int i = 0; i < length8; i++)
  {
    int i8 = i * 8;
    long k = ((long)data[i8 + 0] & 0xff)           //
             + (((long)data[i8 + 1] & 0xff) << 8)  //
             + (((long)data[i8 + 2] & 0xff) << 16) //
             + (((long)data[i8 + 3] & 0xff) << 24) //
             + (((long)data[i8 + 4] & 0xff) << 32) //
             + (((long)data[i8 + 5] & 0xff) << 40) //
             + (((long)data[i8 + 6] & 0xff) << 48) //
             + (((long)data[i8 + 7] & 0xff) << 56);

    k *= m;
    k ^= k >> r;
    k *= m;

    h ^= k;
    h *= m;
  }

  switch (length % 8)
  {
  case 7:
    h ^= (long)(data[(length & ~7) + 6] & 0xff) << 48;
  case 6:
    h ^= (long)(data[(length & ~7) + 5] & 0xff) << 40;
  case 5:
    h ^= (long)(data[(length & ~7) + 4] & 0xff) << 32;
  case 4:
    h ^= (long)(data[(length & ~7) + 3] & 0xff) << 24;
  case 3:
    h ^= (long)(data[(length & ~7) + 2] & 0xff) << 16;
  case 2:
    h ^= (long)(data[(length & ~7) + 1] & 0xff) << 8;
  case 1:
    h ^= (long)(data[length & ~7] & 0xff);
    h *= m;
  }

  h ^= h >> r;
  h *= m;
  h ^= h >> r;

  return h;
}

// char *Utils::md5Bytes(string_ptr  text)
// {
//   MessageDigest msgDigest = NULL;

//   try
//   {
//     msgDigest = MessageDigest.getInstance("MD5");
//   }
//   catch (NoSuchAlgorithmException e)
//   {
//     throw new IllegalStateException("System doesn't support MD5 algorithm.");
//   }

//   msgDigest.update(text.getBytes());

//   char *bytes = msgDigest.digest();

//   return bytes;
// }

// string_ptr  Utils::md5(string_ptr  text)
// {
//   char *bytes = md5Bytes(text);
//   return HexBin::encode(bytes, false);
// }

void Utils::putLong(char *b, int off, long val)
{
  b[off + 7] = (char)(val >> 0);
  b[off + 6] = (char)(val >> 8);
  b[off + 5] = (char)(val >> 16);
  b[off + 4] = (char)(val >> 24);
  b[off + 3] = (char)(val >> 32);
  b[off + 2] = (char)(val >> 40);
  b[off + 1] = (char)(val >> 48);
  b[off + 0] = (char)(val >> 56);
}

// bool Utils::equals(Object_ptr  a, Object_ptr  b) {
//     return (a == b) || (a != NULL && a.equals(b));
// }

string_ptr Utils::hex(int hash)
{
  char bytes[4];

  bytes[3] = (char)(hash);
  bytes[2] = (char)(hash >> 8);
  bytes[1] = (char)(hash >> 16);
  bytes[0] = (char)(hash >> 24);

  char chars[8];
  for (int i = 0; i < 4; ++i)
  {
    char b = bytes[i];

    int a = b & 0xFF;
    int b0 = a >> 4;
    int b1 = a & 0xf;

    chars[i * 2] = (char)(b0 + (b0 < 10 ? 48 : 55));
    chars[i * 2 + 1] = (char)(b1 + (b1 < 10 ? 48 : 55));
  }

  return make_string_ptr(chars);
}

string_ptr Utils::hex(long hash)
{
  char bytes[8];

  bytes[7] = (char)(hash);
  bytes[6] = (char)(hash >> 8);
  bytes[5] = (char)(hash >> 16);
  bytes[4] = (char)(hash >> 24);
  bytes[3] = (char)(hash >> 32);
  bytes[2] = (char)(hash >> 40);
  bytes[1] = (char)(hash >> 48);
  bytes[0] = (char)(hash >> 56);

  char chars[16];
  for (int i = 0; i < 8; ++i)
  {
    char b = bytes[i];

    int a = b & 0xFF;
    int b0 = a >> 4;
    int b1 = a & 0xf;

    chars[i * 2] = (char)(b0 + (b0 < 10 ? 48 : 55));
    chars[i * 2 + 1] = (char)(b1 + (b1 < 10 ? 48 : 55));
  }

  return make_string_ptr(chars);
}

string_ptr Utils::hex_t(long hash)
{
  char bytes[8];

  bytes[7] = (char)(hash);
  bytes[6] = (char)(hash >> 8);
  bytes[5] = (char)(hash >> 16);
  bytes[4] = (char)(hash >> 24);
  bytes[3] = (char)(hash >> 32);
  bytes[2] = (char)(hash >> 40);
  bytes[1] = (char)(hash >> 48);
  bytes[0] = (char)(hash >> 56);

  char chars[18];
  chars[0] = 'T';
  chars[1] = '_';
  for (int i = 0; i < 8; ++i)
  {
    char b = bytes[i];

    int a = b & 0xFF;
    int b0 = a >> 4;
    int b1 = a & 0xf;

    chars[i * 2 + 2] = (char)(b0 + (b0 < 10 ? 48 : 55));
    chars[i * 2 + 3] = (char)(b1 + (b1 < 10 ? 48 : 55));
  }

  return make_string_ptr(chars);
}

long Utils::fnv_64(string_ptr input)
{
  return FnvHash::fnv1a_64(input);
}

long Utils::fnv_64_lower(string_ptr key)
{
  return FnvHash::fnv1a_64_lower(key);
}

long Utils::fnv_32_lower(string_ptr key)
{
  return FnvHash::fnv_32_lower(key);
}

// void Utils::loadFromFile(string_ptr  path, Set<string_ptr > set) {
//     InputStream is = NULL;
//     BufferedReader reader = NULL;
//     try {
//         is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
//         reader = new BufferedReader(new InputStreamReader(is));
//         for (; ; ) {
//             string_ptr  line = reader.readLine();
//             if (line == NULL) {
//                 break;
//             }

//             line = line.trim().toLowerCase();

//             if (line.length() == 0) {
//                 continue;
//             }
//             set.add(line);
//         }
//     } catch (Exception ex) {
//         // skip
//     } finally {
//         JdbcUtils.close(is);
//         JdbcUtils.close(reader);
//     }
// }
